#include "Common.h"
#include "VertexFactory.h"
#include "Render.h"
#include "VertexIndexData.h"
#include "Shader.h"
#include "MaterialManager.h"
#include "Renderable.h"
#include "RenderView.h"


VertexFactory::VertexFactory(const VertexData* vertexData)
	: mVertexData(vertexData)
	, mDeclaration(NULL)
	, mPositionDeclaration(NULL)
{
}

VertexFactory::~VertexFactory()
{
	SAFE_RELEASE(mDeclaration);
	SAFE_RELEASE(mPositionDeclaration);
}

void VertexFactory::Set(bool positionOnly) const
{
	mVertexData->Set(positionOnly);
}

void VertexFactory::InitDeclaration(D3DVERTEXELEMENT9* vertexElements)
{
	Render::Instance().getDevice()->CreateVertexDeclaration(vertexElements, &mDeclaration);
}

void VertexFactory::InitPositionDeclaration(D3DVERTEXELEMENT9* vertexElements)
{
	Render::Instance().getDevice()->CreateVertexDeclaration(vertexElements, &mPositionDeclaration);
}

//
// LocalVertexFactory
//
const VertexFactoryType LocalVertexFactory::StaticType(
	"Local",
	&LocalVertexFactory::ShouldCache,
	&LocalVertexFactory::Define,
	&LocalVertexFactory::CreateParam);

void LocalVertexFactory::LocalVertexFactoryParam::Bind(Shader* vertexShader)
{
	mWorldViewProjMatrixParam.Bind(vertexShader, "WorldViewProjMatrix");
}

void LocalVertexFactory::LocalVertexFactoryParam::SetRenderable(RenderView* view, Renderable* renderable)
{
	Matrix worldViewProjMatrix = renderable->getWorldMatrix() * view->ViewProjMatrix;
	mWorldViewProjMatrixParam.SetVertexShaderValue(worldViewProjMatrix);
}

void LocalVertexFactory::Init()
{
	// vertex declaration
	D3DVERTEXELEMENT9 vertexElements[] = {
		{0,		0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
		{1,		0, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
		{1,	    4, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0},
		{1,	    8, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
		D3DDECL_END()};
	InitDeclaration(vertexElements);
	
	// position vertex declaration
	D3DVERTEXELEMENT9 positionVertexElements[] = {
		{0, 0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
		D3DDECL_END()};
	InitPositionDeclaration(positionVertexElements);
}

//
// GPUSkinVertexFactory
//
const VertexFactoryType GPUSkinVertexFactory::StaticType(
	"GPUSkin",
	&GPUSkinVertexFactory::ShouldCache,
	&GPUSkinVertexFactory::Define,
	&GPUSkinVertexFactory::CreateParam);

void GPUSkinVertexFactory::GPUSkinVertexFactoryParam::Bind(Shader* vertexShader)
{
	mWorldViewProjMatrixParam.Bind(vertexShader, "WorldViewProjMatrix");
	mBoneMatricesParam.Bind(vertexShader, "BoneMatrices");
}

void GPUSkinVertexFactory::GPUSkinVertexFactoryParam::SetRenderable(RenderView* view, Renderable* renderable)
{
	Matrix worldViewProjMatrix = renderable->getWorldMatrix() * view->ViewProjMatrix;
	mWorldViewProjMatrixParam.SetVertexShaderValue(worldViewProjMatrix);

	if (mBoneMatricesParam.getBinded())
	{
		static Matrix3x4 boneMatrices[MAX_BONES];
		size_t numTransform = renderable->GetGpuSkinMatrix(boneMatrices);

		ShaderParameter::SetVertexShaderValue(
			mBoneMatricesParam.getStartRegister(),
			boneMatrices[0].M[0],
			numTransform * 3);
	}
}

bool GPUSkinVertexFactory::ShouldCache(const class Material* mat)
{
	// only cache the default material.
	if (MaterialManager::Instance().getDefaultMaterial() == mat)
		return true;

	return false;
}

void GPUSkinVertexFactory::Define(ShaderDefineMap* defineMap)
{
	defineMap->AddDefine("GPUSkining", "1");
}

void GPUSkinVertexFactory::Init()
{
	// vertex declaration
	D3DVERTEXELEMENT9 vertexElements[] = {
		{0,		0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
		{0,	    12, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDINDICES, 0},
		{0,	    16, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 0},
		{1,		0, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
		{1,	    4, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0},
		{1,	    8, D3DDECLTYPE_FLOAT16_2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
		D3DDECL_END()};
	InitDeclaration(vertexElements);
	
	// position vertex declaration
	D3DVERTEXELEMENT9 positionVertexElements[] = {
		{0,		0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
		{0,	    12, D3DDECLTYPE_UBYTE4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDINDICES, 0},
		{0,	    16, D3DDECLTYPE_UBYTE4N, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 0},
		D3DDECL_END()};
	InitPositionDeclaration(positionVertexElements);
}

